<template>
  <view class="v-tabs">
    <scroll-view
      :id="getDomId"
      :scroll-x="scroll"
      :scroll-left="scroll ? scrollLeft : 0"
      :scroll-with-animation="scroll"
      :style="{ position: fixed ? 'fixed' : 'relative', zIndex }">
      <view
        class="v-tabs__container"
        :style="{
          display: scroll ? 'inline-flex' : 'flex',
          whiteSpace: scroll ? 'nowrap' : 'normal',
          background: bgColor,
          height,
          padding
        }">
        <view
          :class="['v-tabs__container-item', { disabled: !!v.disabled }, { active: current == i }]"
          v-for="(v, i) in tabs"
          :key="i"
          :style="{
            color: current == i ? activeColor : color,
            fontSize: current == i ? fontSize : fontSize,
            fontWeight: bold && current == i ? 'bold' : '',
            justifyContent: !scroll ? 'center' : '',
            flex: scroll ? '' : 1,
            padding: paddingItem
          }"
          @click="change(i,'tab')">
          <slot :row="v" :index="i">{{ field ? v[field] : v }}</slot>
        </view>
        <template v-if="!!tabs.length">
            
            <view v-if="lineSlot"
                class="lineSlot"
                :class="['v-tabs__container-line', { animation: lineAnimation }]"
                :style="{
                transform: `translate3d(${lineLeft}px, 0, 0)`
                }"
            >
                <slot name="line"/>
            </view>
            <view
                v-else-if="!pills"
                :class="['v-tabs__container-line', { animation: lineAnimation }]"
                :style="{
                background: lineColor,
                width: lineWidth + 'px',
                height: lineHeight,
                borderRadius: lineRadius,
                transform: `translate3d(${lineLeft}px, 0, 0)`
                }" />
            <view
                v-else
                :class="['v-tabs__container-pills', { animation: lineAnimation }]"
                :style="{
                background: pillsColor,
                borderRadius: pillsBorderRadius,
                width: currentWidth + 'px',
                transform: `translate3d(${pillsLeft}px, 0, 0)`,
                height
                }" />
        </template>
      </view>
    </scroll-view>
    <!-- fixed 的站位高度 -->
    <view class="v-tabs__placeholder" :style="{ height: fixed ? height : '0', padding }"></view>
    <view class="v-tabs__content" v-if="tabsSwiper">
      <swiper class="activity-swiper" 
      v-if="!swiperType"
      :current="value" 
      :duration="duration"
      :circular="circular"
      @change="onSwiperChange"
      :style="{ height: contentHeight ? contentHeight : `calc(100vh - ${height} - ${paddingBottom})`, paddingBottom: paddingBottom, paddingTop: paddingTop}">
        <swiper-item class="activity-swiper-item" v-for="(item,index) in tabs" :key="item.key" :item-id="item.key"
          :style="{ paddingBottom: 0, paddingTop: 0 }">
          <scroll-view scroll-y class="scroll-list" @scrolltolower="onBottom" :style="{ height: '100%' }">
            <slot :index="index" name="swiperContent"/>
          </scroll-view>
        </swiper-item>
      </swiper>
      <view class="data_list" @touchstart="touchStart" @touchend="touchEnd" :animation="animationData" v-else>
				<view class="data_list_content" v-for="(item,index) in tabs" :key="index">
          <scroll-view scroll-y class="scroll-list" @scrolltolower="onBottom" :style="{ height: contentHeight ? contentHeight : `calc(100vh - ${height} - ${paddingBottom})`, paddingBottom: paddingBottom, paddingTop: paddingTop}">
            <slot :index="index" name="swiperContent"/>
          </scroll-view>
        </view>
			</view>
    </view>
  </view>
</template>

<script>
import { startMicroTask } from './utils'
import props from './props'
/**
 * v-tabs
 * @property {Number} value 选中的下标
 * @property {Array} tabs tabs 列表
 * @property {String} bgColor = '#fff' 背景颜色
 * @property {String} color = '#333' 默认颜色
 * @property {String} activeColor = '#2979ff' 选中文字颜色
 * @property {String} fontSize = '28rpx' 默认文字大小
 * @property {String} activeFontSize = '28rpx' 选中文字大小
 * @property {Boolean} bold = [true | false] 选中文字是否加粗
 * @property {Boolean} scroll = [true | false] 是否滚动
 * @property {String} height = '60rpx' tab 的高度
 * @property {String} lineHeight = '10rpx' 下划线的高度
 * @property {String} lineColor = '#2979ff' 下划线的颜色
 * @property {Number} lineScale = 0.5 下划线的宽度缩放比例
 * @property {String} lineRadius = '10rpx' 下划线圆角
 * @property {Boolean} pills = [true | false] 是否胶囊样式
 * @property {String} pillsColor = '#2979ff' 胶囊背景色
 * @property {String} pillsBorderRadius = '10rpx' 胶囊圆角大小
 * @property {String} field 如果是对象，显示的键名
 * @property {Boolean} fixed = [true | false] 是否固定
 * @property {String} paddingItem = '0 22rpx' 选项的边距
 * @property {Boolean} lineAnimation = [true | false] 下划线是否有动画
 * @property {Number} zIndex = 1993 默认层级
 * @property {Boolean} lineSlot true 是否自定义底部滑块
 * @property {Boolean} tabsSwiper false 是否使用swiper
 * @property {Boolean} swiperType false swiper类型
 * @property {Boolean} circular false 是否采用衔接滑动，即播放到末尾后重新回到开头
 * @property {String} paddingBottom 0rpx swiper下边padding 
 * @property {String} paddingTop 0rpx swiper上边padding
 * @property {Number} duration 500 swiper滑动动画时长
 * @property {String} contentHeight '' swiper内容高度
 *
 * @event {Function(current)} change 改变标签触发
 */
export default {
  name: 'kTabsSwiper',
  props,
  data() {
    return {
      lineWidth: 30,
      currentWidth: 0, // 当前选项的宽度
      lineLeft: 0, // 滑块距离左侧的位置
      pillsLeft: 0, // 胶囊距离左侧的位置
      scrollLeft: 0, // 距离左边的位置
      container: { width: 0, height: 0, left: 0, right: 0 }, // 容器的宽高，左右距离
      current: 0, // 当前选中项
      scrollWidth: 0, // 可以滚动的宽度
      lineSlotWidth:0,//自定义底部的宽度
      startX: 0,
      startY: 0,
      animationData: {}, // 动画
    }
  },
  computed: {
    getDomId() {
      const len = 16
      const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
      const maxPos = $chars.length
      let pwd = ''
      for (let i = 0; i < len; i++) {
        pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
      }
      return `xfjpeter_${pwd}`
    }
  },
  watch: {
    value: {
      immediate: true,
      handler(newVal) {
        this.current = newVal
        this.$nextTick(this.update)
      }
    },
  },
  methods: {
      initScroll(){
        this.scrollY = new Array(10).fill(0);
      },
      touchStart(event) {
				this.startX = event.touches[0].pageX;
				this.startY = event.touches[0].pageY;
			},
			touchEnd(event) {
				let deltaX = event.changedTouches[0].pageX - this.startX;  
				let deltaY = event.changedTouches[0].pageY - this.startY;
				if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX)>60) {
					if (deltaX < 0) { //往左
						if (this.current == this.tabs.length - 1) {
              if(this.circular){
                this.change(0);
              }else{
                return
              }
						} else {
              this.change(this.current * 1 + 1);
						}
            
						this.animate();
						
					} else if (deltaX > 0) { //往右
						if (this.current == 0) {
              if(this.circular){
                this.change(this.tabs.length - 1);
              }else{
                return
              }
						} else {
              this.change(this.current * 1 - 1);
						}
            
						this.animate();
						
					} else { // 挪动距离0
						
					}
				}else{
					
				}
			},
    onSwiperChange(e) {
      this.change(e.detail.current);
    },
    onBottom() {
      this.$emit("load",this.current)
    },
    animate(){
      this.animation.translateX(-this.swiperWidthPx * this.current +"px").step({
          duration: this.duration
      })
      this.animationData = this.animation.export()
      setTimeout(() => {
        this.animationData = {}
      }, this.duration)
    },
    // 切换事件
    change(index,source) {
      
      const isDisabled = !!this.tabs[index].disabled
      if (this.current !== index && !isDisabled) {
        this.current = index
        this.$emit('input', index)
        this.$emit('change', index)
      }
      //如果是tab切换
      if(this.swiperType && source == 'tab'){
        this.animate();
      }
    },
    createQueryHandler() {
      const query = uni
        .createSelectorQuery()
        // #ifndef MP-ALIPAY
        .in(this)
      // #endif

      return query
    },
    update() {
      const _this = this
      startMicroTask(() => {
        // 没有列表的时候，不执行
        if (!this.tabs.length) return
        _this
          .createQueryHandler()
          .select(`#${this.getDomId}`)
          .boundingClientRect(data => {
            const { width, height, left, right } = data || {}
            // 获取容器的相关属性
            this.container = { width, height, left, right: right - width }
            _this.lineScrollWidth();
            _this.calcScrollWidth()
            _this.setScrollLeft()
            _this.setLine()
            if(this.swiperType){
              _this.getSwiperWidth();
              _this.initScroll();
              // 创建动画实例
              _this.animation = uni.createAnimation({
                timingFunction: 'ease',
                duration: 120
              })
            }
          })
          .exec()
      })
    },
    getSwiperWidth(){
      this.createQueryHandler()
          .select(`.v-tabs__content`)
          .boundingClientRect(data => {
            if (!data) return;
            this.swiperWidthPx = data.width;
          })
          .exec()
    },
    // 计算可以滚动的宽度
    calcScrollWidth(callback) {
      const view = this.createQueryHandler().select(`#${this.getDomId}`)
      view.fields({ scrollOffset: true })
      view
        .scrollOffset(res => {
          if (typeof callback === 'function') {
            callback(res)
          } else {
            // 获取滚动条的宽度
            this.scrollWidth = res.scrollWidth
          }
        })
        .exec()
    },
    // 设置滚动条滚动的进度
    setScrollLeft() {
      this.calcScrollWidth(res => {
        // 动态读取 scrollLeft
        let scrollLeft = res.scrollLeft
        this.createQueryHandler()
          .select(`#${this.getDomId} .v-tabs__container-item.active`)
          .boundingClientRect(data => {
            if (!data) return
            // 除开当前选项外容器的一半宽度
            let curHalfWidth = (this.container.width - data.width) / 2
            let scrollDiff = this.scrollWidth - this.container.width
            // 在原有滚动条的基础上 + (当前元素距离左侧的距离 - 计算的一半宽度) - 容器的外边距之类的
            scrollLeft += data.left - curHalfWidth - this.container.left
            // 已经滚动在左侧了
            if (scrollLeft < 0) scrollLeft = 0
            // 已经超出右侧了
            else if (scrollLeft > scrollDiff) scrollLeft = scrollDiff
            this.scrollLeft = scrollLeft
          })
          .exec()
      })
    },
    lineScrollWidth(){
      this.createQueryHandler()
          .select(`#${this.getDomId} .lineSlot`)
          .boundingClientRect(data => {
            if (!data) return;
            this.lineSlotWidth = data.width;
          })
          .exec()
    },
    setLine() {
      this.calcScrollWidth(res => {
        const scrollLeft = res.scrollLeft
        this.createQueryHandler()
          .select(`#${this.getDomId} .v-tabs__container-item.active`)
          .boundingClientRect(data => {
            if (!data) return
            if(this.lineSlot){
                this.lineWidth = data.width * this.lineScale
                this.lineLeft = scrollLeft + data.left + data.width / 2 - this.lineSlotWidth / 2  - this.container.left
            }else if (this.pills) {
              this.currentWidth = data.width
              this.pillsLeft = scrollLeft + data.left - this.container.left
            } else {
              this.lineWidth = data.width * this.lineScale
              this.lineLeft = scrollLeft + data.left + (data.width - data.width * this.lineScale) / 2 - this.container.left
            }
          })
          .exec()
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.v-tabs {
  width: 100%;
  box-sizing: border-box;
  overflow: hidden;

  /* #ifdef H5 */
  ::-webkit-scrollbar {
    display: none;
  }
  /* #endif */

  &__container {
    min-width: 100%;
    position: relative;
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
    overflow: hidden;

    &-item {
      flex-shrink: 0;
      display: flex;
      align-items: center;
      height: 100%;
      position: relative;
      z-index: 10;
      transition: all 0.3s;
      white-space: nowrap;

      &.disabled {
        opacity: 0.5;
        color: #999;
      }
    }

    &-line {
      position: absolute;
      left: 0;
      bottom: 0;
    }

    &-pills {
      position: absolute;
      z-index: 9;
    }

    &-line,
    &-pills {
      &.animation {
        transition: all 0.3s linear;
      }
    }
  }
}
.data_list{
  display: flex;
  &_content{
      flex-shrink: 0;
      width: 100%;
  }
}
</style>
